package com.zt.teacher.shiro;

import com.auth0.jwt.exceptions.TokenExpiredException;
import com.zt.common.common.ApiException;
import com.zt.common.common.CodeMsg;
import com.zt.common.pojo.Teachers;
import com.zt.common.pojo.common.JwtToken;
import com.zt.common.utils.JwtUtil;
import com.zt.common.utils.RedisUtil;
import com.zt.teacher.service.TeacherService;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.springframework.beans.factory.annotation.Autowired;


/**
 * description:
 * author:zt
 * date:2021-10-23
 */

public class JwtRealm extends AuthorizingRealm {

    @Autowired
    private RedisUtil redisUtil;
    @Autowired
    private TeacherService teacherService;

    /**
     * 限定只能处理自定义JWTtoken
     * @param token
     * @return
     */
    @Override
    public boolean supports(AuthenticationToken token) {
        return token instanceof JwtToken;
    }

    /**
     * 授权
     * @param principals 授权信息
     * @return
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        Teachers teacher = (Teachers) SecurityUtils.getSubject().getPrincipal();

        SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
        simpleAuthorizationInfo.addRole(teacher.getType()==1?"normal":"admin");

        return simpleAuthorizationInfo;
    }

    /**
     * 认证 分为以下几种情况
     * 1.access token 没过期，那么refresh token肯定也没有过期 直接返回认证成功
     * 2.refresh token没过期，access token过期， 返回TokenExpiredException 给jwtFilter刷新token
     * 3.refresh token 和 access token都过期 返回AuthenticationException 给jwtFilter直接返回前端错误信息
     * 4.其他错误 同样直接返回前端错误信息
     * @param auth 传入的jwtToken
     * @return
     * @throws AuthenticationException
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken auth) throws AuthenticationException {

        //自己封装的jwtToken：Credentials为交给前端的jwt原生token
        JwtToken jwtToken = (JwtToken) auth;
        String token = (String) jwtToken.getCredentials();
        String teacherId = null;
        try {
            teacherId = JwtUtil.decodedGetID(token);
        }catch (Exception e){
            throw new AuthenticationException("token非法，不是规范的token，可能被篡改了，或者过期了");
        }
        if (teacherId == null){
            throw new AuthenticationException("token中无用户名");
        }

        Teachers teacher = teacherService.findTeacherById(teacherId);
        if (teacher == null)
                throw new AuthenticationException("该用户不存在");

        //开始认证，只要AccessToken没有过期，或者refreshToken的时间节点和AccessToken一致即可
        if (redisUtil.hasKey(teacherId)){
            //判断AccessToken有无过期
            if (!JwtUtil.verify(token)){
                throw new TokenExpiredException("access token认证失效，token过期，重新登陆");
            }else {
                //判断AccessToken和refreshToken的时间节点是否一致
                /*redis当中存refresh token的值 应该与 该token（access token） claim中的expire 一致
                    因为每一次签发access token 都会刷新refresh token
                 */
                long current= Long.parseLong(redisUtil.get(teacherId).toString());
                String expire = JwtUtil.getExpire(token);
                if (current == Long.parseLong(expire)){
                    return new SimpleAuthenticationInfo(teacher,teacher,getName());
                }else{
                    throw new AuthenticationException("未刷新access token，请重新登录！");
                }
            }
        }else{
            //redis中的refresh token已过期 需要重新登录签发
            throw new ApiException(CodeMsg.TOKEN_EXPIRE);
        }
    }
}
